﻿/* Open source distributed without warranties of any kind. */

namespace Transcriber
{
    #region Using
    using System;
    using System.Diagnostics;
    using System.Drawing;
    using System.IO;
    using System.Reflection;
    using System.Text.RegularExpressions;
    using System.Windows.Forms;
    using Transcriber.Api;
    using Transcriber.Api.Properties;
    #endregion

    /// <summary>
    /// Transcriber main window <see cref="Form"/>
    /// </summary>
    public partial class frmTranscriber : Form, ITranscriberApplication
    {
        private static DataFormats.Format rtfFormat = DataFormats.GetFormat(DataFormats.Rtf);
        private static DataFormats.Format textFormat = DataFormats.GetFormat(DataFormats.Text);
        private static Regex courierRegex = new Regex("^Courier New$", RegexOptions.Compiled);

        private const int DefaultFontSize = 12;

        private bool sourceChanged;
        private bool destinationChanged;
        private FileInfo sourceFile;
        private FileInfo destinationFile;
        private bool isInitialized;

        /// <summary>
        /// Initializes a new instance of the <see cref="frmTranscriber"/> class.
        /// </summary>
        public frmTranscriber()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Display the "About..." dialog.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            frmAboutTranscriber about = new frmAboutTranscriber();

            about.ShowDialog();
        }

        /// <summary>
        /// Display the help file table of contents contents.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void contentsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FileInfo file = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Transcriber User Guide.xps"));

            if (!file.Exists)
            {
                MessageBox.Show("User guide not found.", "Error");
                return;
            }

            Process.Start(file.FullName);
        }

        /// <summary>
        /// Handles the Click event of the btnSourceNew control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnSourceNew_Click(object sender, EventArgs e)
        {
            this.NewSource();
        }

        /// <summary>
        /// Handles the Click event of the btnSourceOpen control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnSourceOpen_Click(object sender, EventArgs e)
        {
            this.NewSource();
            this.OpenSource();
        }

        /// <summary>
        /// Handles the Click event of the btnSourceSave control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnSourceSave_Click(object sender, EventArgs e)
        {
            try
            {
                this.SaveSource();
            }
            catch (AbortOperationException)
            {
            }
            catch
            {
                MessageBox.Show("Error saving file.", "Error");
            }
        }

        /// <summary>
        /// Saves the source text box to a file.
        /// </summary>
        private void SaveSource()
        {
            this.sourceFile = this.SaveFile(this.sourceFile, "Save Source", this.rtSource);
            this.sourceChanged = false;
        }

        /// <summary>
        /// Clear the source text box.
        /// </summary>
        private void NewSource()
        {
            if (this.sourceChanged)
            {
                switch (MessageBox.Show("Source has changed, save?", "Source Changed", MessageBoxButtons.YesNoCancel))
                {
                    case DialogResult.Cancel:
                        throw new AbortOperationException();

                    case DialogResult.No:
                        break;

                    default:
                        this.SaveSource();
                        break;
                }
            }

            rtSource.Clear();
        }

        /// <summary>
        /// Loads a file into the source text box.
        /// </summary>
        private void OpenSource()
        {
            try
            {
                this.sourceFile = this.LoadFile(this.sourceFile, "Open Source", this.rtSource);
                this.sourceChanged = false;
            }
            catch (AbortOperationException)
            {
            }
            catch
            {
                MessageBox.Show("Error opening source file.", "Error Opening Source");
            }
        }

        /// <summary>
        /// Saves the given text box to a file.
        /// </summary>
        private FileInfo SaveFile(FileInfo fileInfo, string title, RichTextBox richTextBox)
        {
            if (null == fileInfo)
            {
                return this.SaveAs(title, richTextBox);
            }

            frmTranscriber.SaveRichTextBoxToFile(fileInfo, richTextBox);
            return fileInfo;
        }

        /// <summary>
        /// Saves the rich text box to a file.
        /// </summary>
        /// <param name="fileInfo">The file info.</param>
        /// <param name="richTextBox">The rich text box.</param>
        private static void SaveRichTextBoxToFile(FileInfo fileInfo, RichTextBox richTextBox)
        {
            if (fileInfo.Exists)
            {
                fileInfo.Delete();
            }

            RichTextBoxStreamType streamType = RichTextBoxStreamType.UnicodePlainText;

            if (fileInfo.FullName.ToLower().EndsWith(".rtf"))
            {
                streamType = RichTextBoxStreamType.RichNoOleObjs;
            }

            richTextBox.SaveFile(fileInfo.FullName, streamType);
        }

        /// <summary>
        /// Handles the Click event of the btnSourcePrint control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnSourcePrint_Click(object sender, EventArgs e)
        {
            this.Print(rtSource);
        }

        /// <summary>
        /// Handles the Click event of the btnSourceCut control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnSourceCut_Click(object sender, EventArgs e)
        {
            this.rtSource.Cut();
        }

        /// <summary>
        /// Handles the Click event of the btnSourceCopy control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnSourceCopy_Click(object sender, EventArgs e)
        {
            this.rtSource.Copy();
        }

        /// <summary>
        /// Handles the Click event of the btnSourcePaste control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnSourcePaste_Click(object sender, EventArgs e)
        {
            this.Paste(this.rtSource);
        }

        /// <summary>
        /// Pastes into the given rich text box.
        /// </summary>
        /// <param name="richTextBox">The rich text box.</param>
        private void Paste(RichTextBox richTextBox)
        {
            if (richTextBox.CanPaste(rtfFormat))
            {
                richTextBox.Paste(rtfFormat);
            }
            else if (richTextBox.CanPaste(textFormat))
            {
                richTextBox.Paste(textFormat);
            }
        }

        /// <summary>
        /// Handles the Click event of the btnDestinationNew control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnDestinationNew_Click(object sender, EventArgs e)
        {
            this.NewDestination();
        }

        /// <summary>
        /// Clears the destination text box.
        /// </summary>
        private void NewDestination()
        {
            if (this.destinationChanged)
            {
                switch (MessageBox.Show("Destination has changed, save?", "Destination Changed", MessageBoxButtons.YesNoCancel))
                {
                    case DialogResult.Cancel:
                        throw new AbortOperationException();

                    case DialogResult.No:
                        break;

                    default:
                        this.SaveDestination();
                        break;
                }
            }

            this.rtDestination.Clear();
        }

        /// <summary>
        /// Handles the Click event of the btnDestinationOpen control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnDestinationOpen_Click(object sender, EventArgs e)
        {
            this.NewDestination();
            this.OpenDestination();
        }

        /// <summary>
        /// Loads a file into the destination text box.
        /// </summary>
        private void OpenDestination()
        {
            try
            {
                this.destinationFile = this.LoadFile(this.destinationFile, "Open Destination", this.rtDestination);
                this.destinationChanged = false;
            }
            catch (AbortOperationException)
            {
            }
            catch
            {
                MessageBox.Show("Error opening destination file.", "Error Opening Destination");
            }
        }

        /// <summary>
        /// Loads the specified file into the given text box..
        /// </summary>
        /// <param name="fileInfo">The file info.</param>
        /// <param name="title">The title.</param>
        /// <param name="richTextBox">The rich text box.</param>
        /// <returns>The file info.</returns>
        private FileInfo LoadFile(FileInfo fileInfo, string title, RichTextBox richTextBox)
        {
            if (null == fileInfo)
            {
                this.openFileDialog1.Title = title;
                this.openFileDialog1.Filter = "Rich text files (*.rtf)|*.rtf|Plain text files (*.txt)|*.txt|All files (*.*)|*.*";
                this.openFileDialog1.CheckFileExists = true;

                switch (this.openFileDialog1.ShowDialog())
                {
                    case DialogResult.Cancel:
                        throw new AbortOperationException();

                    default:
                        fileInfo = new FileInfo(openFileDialog1.FileName);
                        break;
                }
            }

            if (!fileInfo.Exists)
            {
                throw new AbortOperationException();
            }

            richTextBox.LoadFile(fileInfo.FullName);

            return fileInfo;
        }

        /// <summary>
        /// Handles the Click event of the btnDestinationSave control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnDestinationSave_Click(object sender, EventArgs e)
        {
            try
            {
                this.SaveDestination();
            }
            catch (AbortOperationException)
            {
            }
            catch
            {
                MessageBox.Show("Error saving file.", "Error");
            }
        }

        /// <summary>
        /// Saves the destination text box to a file.
        /// </summary>
        private void SaveDestination()
        {
            this.destinationFile = this.SaveFile(this.destinationFile, "Save Destination", this.rtDestination);
            this.destinationChanged = false;
        }

        /// <summary>
        /// Prints the given rich text box.
        /// </summary>
        /// <param name="richTextBox">The rich text box.</param>
        private void Print(RichTextBox richTextBox)
        {
            MessageBox.Show("Not yet implemented", "Print");
        }

        /// <summary>
        /// Handles the Click event of the btnDestinationPrint control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnDestinationPrint_Click(object sender, EventArgs e)
        {
            this.Print(this.rtDestination);
        }

        /// <summary>
        /// Handles the Click event of the btnDestinationCut control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnDestinationCut_Click(object sender, EventArgs e)
        {
            this.rtDestination.Cut();
        }

        /// <summary>
        /// Handles the Click event of the btnDestinationCopy control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnDestinationCopy_Click(object sender, EventArgs e)
        {
            this.rtDestination.Copy();
        }

        /// <summary>
        /// Handles the Click event of the btnDestinationPaste control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnDestinationPaste_Click(object sender, EventArgs e)
        {
            this.Paste(this.rtDestination);
        }

        /// <summary>
        /// Handles the Click event of the exitToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Handles the TextChanged event of the rtSource control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void rtSource_TextChanged(object sender, EventArgs e)
        {
            this.sourceChanged = true;
        }

        /// <summary>
        /// Handles the TextChanged event of the rtDestination control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void rtDestination_TextChanged(object sender, EventArgs e)
        {
            this.destinationChanged = true;
        }

        /// <summary>
        /// Handles the FormClosing event of the frmTranscriber control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.FormClosingEventArgs"/> instance containing the event data.</param>
        private void frmTranscriber_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                Settings.Default.Save();
                if (this.sourceChanged)
                {
                    switch (MessageBox.Show("Source text changed, save?", "Source Changed", MessageBoxButtons.YesNoCancel))
                    {
                        case DialogResult.Cancel:
                            e.Cancel = true;
                            return;

                        case DialogResult.Yes:
                            this.SaveSource();
                            break;

                        default:
                            break;
                    }
                }

                if (this.destinationChanged)
                {
                    switch (MessageBox.Show("Destination text changed, save?", "Destination Changed", MessageBoxButtons.YesNoCancel))
                    {
                        case DialogResult.Cancel:
                            e.Cancel = true;
                            break;

                        case DialogResult.Yes:
                            this.SaveDestination();
                            break;

                        default:
                            break;
                    }
                }
            }
            catch (AbortOperationException)
            {
                e.Cancel = true;
                return;
            }
            catch
            {
                if (DialogResult.Yes != MessageBox.Show("Error saving files, exit anyway?", "Error Saving Files", MessageBoxButtons.YesNo))
                {
                    e.Cancel = true;
                    return;
                }
            }

            foreach (ITranscriberPlugin plugin in this.lstPlugins.Items)
            {
                plugin.Dispose();
            }

            this.lstPlugins.Items.Clear();
        }

        /// <summary>
        /// Handles the Load event of the frmTranscriber control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void frmTranscriber_Load(object sender, EventArgs e)
        {
            this.rtSource.SelectionFont = Settings.Default.SourceFont;
            this.rtDestination.SelectionFont = Settings.Default.DestinationFont;
            frmTranscriber.PopulateFonts(this.cbSourceFont, Settings.Default.SourceFont);
            frmTranscriber.PopulateFonts(this.cbDestinationFont, Settings.Default.DestinationFont);
            frmTranscriber.PopulateFontSizes(this.cbSourceFontSize, (int)Settings.Default.SourceFont.SizeInPoints);
            frmTranscriber.PopulateFontSizes(this.cbDestinationFontSize, (int)Settings.Default.DestinationFont.SizeInPoints);
            this.LoadPlugins();
            this.rtSource.ContextMenuStrip = this.CreateSourceContextMenu();
            this.rtDestination.ContextMenuStrip = this.CreateDestinationContextMenu();
            string text = Settings.Default.SourceText;

            if ((null != text) && (text.Length > 0))
            {
                switch (text)
                {
                    case "Namarie":
                        this.rtSource.Text = "namárië\r\n(altariello nainië lóriendesse)\r\n\r\nai! laurië lantar lassi súrinen,\r\nyéni únótimë ve rámar aldaron!\r\nyéni ve lintë yuldar avánier\r\nmi oromardi lissë-miruvóreva\r\nandúnë pella, vardo tellumar\r\nnu luini yassen tintilar i eleni\r\nómaryo airetári-lírinen.\r\nsí man i yulma nin enquantuva?\r\n\r\nan sí tintallë varda oiolossëo\r\nve fanyar máryat elentári ortanë\r\nar ilyë tier undulávë lumbulë\r\nar sindanóriello caita mornië i falmalinnar imbë met,\r\nar hísië untúpa calaciryo míri oialë.\r\nsí vanwa ná, rómello vanwa, valimar!\r\nnamárië! nai hiruvalyë valimar!\r\nnai elyë hiruva! namárië!";
                        break;

                    case "Elbereth":
                        this.rtSource.Text = "A Elbereth Gilthoniel,\r\nsilivren penna míriel\r\no menel aglar elenath!\r\nNa-chaered palan-díriel\r\no galadhremmin ennorath, \r\nFanuilos, le linnathon\r\nnef aear, sí nef aearon!";
                        break;

                    case "AshNazg":
                        this.rtSource.Text = "Ash nazg durbatulûk, ash nazg gimbatul,\r\nAsh nazg thrakatulûk agh burzum-ishi krimpatul";
                        break;

                    case "NowIs":
                        this.rtSource.Text = "Now is the time for all good men to come to the aid of their party.";
                        break;

                    default:
                        break;
                }
            }
        }

        /// <summary>
        /// Creates the destination context menu.
        /// </summary>
        /// <returns>The destination context menu.</returns>
        private ContextMenuStrip CreateDestinationContextMenu()
        {
            ContextMenuStrip menu = new ContextMenuStrip();
            menu.Items.Add("Copy", null, new EventHandler(this.mnuDestinationCopy_Click));
            menu.Items.Add("Cut", null, new EventHandler(this.mnuDestinationCut_Click));
            menu.Items.Add("Paste", null, new EventHandler(this.mnuDestinationPaste_Click));
            menu.Items.Add("Paste Plain Text", null, new EventHandler(this.mnuDestinationPastePlainText_Click));
            ITranscriberPlugin plugin = this.lstPlugins.SelectedItem as ITranscriberPlugin;

            if (null != plugin)
            {
                ToolStripItem[] items = plugin.GetDestinationContextMenuItems();

                if ((null != items) && (items.Length > 0))
                {
                    menu.Items.Add(new ToolStripSeparator());
                    foreach (ToolStripItem item in items)
                    {
                        menu.Items.Add(item);
                    }
                }
            }

            return menu;
        }

        /// <summary>
        /// Creates the source context menu.
        /// </summary>
        /// <returns>The source context menu.</returns>
        private ContextMenuStrip CreateSourceContextMenu()
        {
            ContextMenuStrip menu = new ContextMenuStrip();
            menu.Items.Add("Copy", null, new EventHandler(this.mnuSourceCopy_Click));
            menu.Items.Add("Cut", null, new EventHandler(this.mnuSourceCut_Click));
            menu.Items.Add("Paste", null, new EventHandler(this.mnuSourcePaste_Click));
            menu.Items.Add("Paste Plain Text", null, new EventHandler(this.mnuSourcePastePlainText_Click));
            ITranscriberPlugin plugin = this.lstPlugins.SelectedItem as ITranscriberPlugin;

            if (null != plugin)
            {
                ToolStripItem[] items = plugin.GetSourceContextMenuItems();

                if ((null != items) && (items.Length > 0))
                {
                    menu.Items.Add(new ToolStripSeparator());
                    foreach (ToolStripItem item in items)
                    {
                        menu.Items.Add(item);
                    }
                }
            }

            return menu;
        }

        /// <summary>
        /// Loads the plug-in assemblies into the plug-ins list.
        /// </summary>
        private void LoadPlugins()
        {
            bool currentPluginFound = false;

            this.lstPlugins.Items.Clear();
            DirectoryInfo codeBase = new DirectoryInfo(AppDomain.CurrentDomain.SetupInformation.ApplicationBase);

            foreach (FileInfo file in codeBase.GetFiles("*.dll", SearchOption.TopDirectoryOnly))
            {
                try
                {
                    Assembly assembly = Assembly.LoadFile(file.FullName, null);
                    if (assembly.GetCustomAttributes(typeof(TranscriberPluginAssemblyAttribute), true).Length > 0)
                    {
                        object[] constructorArguments = new object[] { this };
                        Type[] constructorArgumentTypes = new Type[] { typeof(ITranscriberApplication) };

                        foreach (Type type in assembly.GetExportedTypes())
                        {
                            if (!type.IsAbstract && typeof(ITranscriberPlugin).IsAssignableFrom(type))
                            {
                                ITranscriberPlugin plugin = (ITranscriberPlugin)type.GetConstructor(constructorArgumentTypes).Invoke(constructorArguments);
                                lstPlugins.Items.Add(plugin);

                                if (Settings.Default.PluginName == plugin.Name)
                                {
                                    lstPlugins.SelectedItem = plugin;
                                    currentPluginFound = true;
                                }
                            }
                        }
                    }
                }
                catch (Exception x)
                {
                    string message = x.Message;

                    if (null != x.InnerException)
                    {
                        message = message + " (" + x.InnerException.Message + ")";
                    }

                    MessageBox.Show(message, "Error Loading Plug-in");
                }
            }

            if (!currentPluginFound && (lstPlugins.Items.Count > 0))
            {
                lstPlugins.SelectedIndex = 0;
            }
        }

        /// <summary>
        /// Gets the font.
        /// </summary>
        /// <param name="fontList">The font list.</param>
        /// <param name="fontSizeList">The font size list.</param>
        /// <param name="boldButton">The "bold" button.</param>
        /// <param name="italicButton">The "italic" button</param>
        /// <returns>The specified font</returns>
        private Font GetFont(ToolStripComboBox fontList, ToolStripComboBox fontSizeList, ToolStripButton boldButton, ToolStripButton italicButton)
        {
            if (!this.isInitialized || (null == fontList.SelectedItem) || (null == fontSizeList.SelectedItem))
            {
                return null;
            }

            float size = Convert.ToSingle((int)fontSizeList.SelectedItem);
            FontStyle style = FontStyle.Regular;

            if (CheckState.Checked == boldButton.CheckState)
            {
                style |= FontStyle.Bold;
            }

            if (CheckState.Checked == italicButton.CheckState)
            {
                style |= FontStyle.Italic;
            }

            FontFamily family = new FontFamily((string)fontList.SelectedItem);

            if (!family.IsStyleAvailable(style))
            {
                foreach (FontStyle s in new FontStyle[] { FontStyle.Bold, FontStyle.Italic, FontStyle.Regular, FontStyle.Strikeout, FontStyle.Underline })
                {
                    if (family.IsStyleAvailable(s))
                    {
                        return new Font(family, size, s);
                    }
                }

                throw new InvalidOperationException("No style found supported by " + family.Name);
            }

            return new Font(family, size, style);
        }

        /// <summary>
        /// Populates the font sizes combo box.
        /// </summary>
        /// <param name="comboBox">The combo box.</param>
        /// <param name="defaultSize">The default font size.</param>
        private static void PopulateFontSizes(ToolStripComboBox comboBox, int defaultSize)
        {
            comboBox.Items.Clear();

            for (int size = 2; size <= 72; size += 2)
            {
                int index = comboBox.Items.Add(size);

                if (size == defaultSize)
                {
                    comboBox.SelectedIndex = index;
                }
            }
        }

        /// <summary>
        /// Populates the given combo boxe with a list of all the font families
        /// currently installed.
        /// </summary>
        /// <param name="comboBox">The combo box.</param>
        /// <param name="selectedFont">The default font.</param>
        private static void PopulateFonts(ToolStripComboBox comboBox, Font selectedFont)
        {
            int selectedIndex = -1;

            comboBox.Items.Clear();

            foreach (FontFamily fontFamily in FontFamily.Families)
            {
                int index = comboBox.Items.Add(fontFamily.Name);

                if (fontFamily.Name == selectedFont.FontFamily.Name)
                {
                    selectedIndex = index;
                    comboBox.SelectedIndex = selectedIndex;
                }
            }

            if (selectedIndex < 0)
            {
                comboBox.SelectedItem = FontFamily.GenericSansSerif.Name;
            }
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the cbDestinationFontSize control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void cbDestinationFontSize_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.ChangeFont(this.rtDestination, this.cbDestinationFont, this.cbDestinationFontSize, this.btnDestinationBold, this.btnDestinationItalic);

            if (null != this.cbDestinationFontSize.SelectedItem)
            {
                Settings.Default.DestinationFont = this.rtDestination.SelectionFont;
            }
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the cbDestinationFont control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void cbDestinationFont_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.ChangeFont(this.rtDestination, this.cbDestinationFont, this.cbDestinationFontSize, this.btnDestinationBold, this.btnDestinationItalic);

            if (null != this.cbDestinationFont.SelectedItem)
            {
                Settings.Default.DestinationFont = this.rtDestination.SelectionFont;
            }
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the cbSourceFontSize control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void cbSourceFontSize_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.ChangeFont(this.rtSource, this.cbSourceFont, this.cbSourceFontSize, this.btnSourceBold, this.btnSourceItalic);

            if (null != this.cbSourceFontSize.SelectedItem)
            {
                Settings.Default.SourceFont = this.rtSource.SelectionFont;
            }
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the cbSourceFont control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void cbSourceFont_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.ChangeFont(this.rtSource, this.cbSourceFont, this.cbSourceFontSize, this.btnSourceBold, this.btnSourceItalic);

            if (null != this.cbSourceFont.SelectedItem)
            {
                Settings.Default.SourceFont = this.rtSource.SelectionFont;
            }
        }

        /// <summary>
        /// Changes the font for the given <see cref="RichTextBox"/>.
        /// </summary>
        /// <param name="richTextBox">The rich text box.</param>
        /// <param name="fontList">The list of font family names.</param>
        /// <param name="fontSizeList">The list of font sizes.</param>
        /// <param name="boldButton">The "bold" button.</param>
        /// <param name="italicButton">The "italic" button</param>
        private void ChangeFont(RichTextBox richTextBox, ToolStripComboBox fontList, ToolStripComboBox fontSizeList, ToolStripButton boldButton, ToolStripButton italicButton)
        {
            if (!this.isInitialized)
            {
                return;
            }

            Font font = this.GetFont(fontList, fontSizeList, boldButton, italicButton);

            if (null != font)
            {
                richTextBox.SelectionFont = font;
            }
        }

        /// <summary>
        /// Handles the Shown event of the frmTranscriber control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void frmTranscriber_Shown(object sender, EventArgs e)
        {
            this.isInitialized = true;
            this.rtSource.Font = this.GetFont(this.cbSourceFont, this.cbSourceFontSize, this.btnSourceBold, this.btnSourceItalic);
            this.rtSource.SelectionFont = this.rtSource.Font;
            this.rtDestination.Font = this.GetFont(this.cbDestinationFont, this.cbDestinationFontSize, this.btnDestinationBold, this.btnDestinationItalic);
            this.rtDestination.SelectionFont = this.rtDestination.Font;
        }

        /// <summary>
        /// Handles the SelectionChanged event of the rtSource control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void rtSource_SelectionChanged(object sender, EventArgs e)
        {
            this.UpdateFontControls(this.rtSource.SelectionFont, this.cbSourceFont, this.cbSourceFontSize, this.btnSourceBold, this.btnSourceItalic);
        }

        /// <summary>
        /// Updates the font controls to match the given font.
        /// </summary>
        /// <param name="font">The current font.</param>
        /// <param name="fontList">The tool strip combo box.</param>
        /// <param name="fontSizeList">The tool strip combo box_3.</param>
        /// <param name="boldButton">The "bold" button.</param>
        /// <param name="italicButton">The "italic" button</param>
        private void UpdateFontControls(Font font, ToolStripComboBox fontList, ToolStripComboBox fontSizeList, ToolStripButton boldButton, ToolStripButton italicButton)
        {
            if (null == font)
            {
                fontList.SelectedItem = null;
                fontSizeList.SelectedItem = null;
                boldButton.CheckState = CheckState.Indeterminate;
                italicButton.CheckState = CheckState.Indeterminate;
            }
            else
            {
                fontList.SelectedItem = font.FontFamily.Name;
                fontSizeList.SelectedItem = (int)font.Size;
                boldButton.CheckState = (int)(font.Style & FontStyle.Bold) == 0 ? CheckState.Unchecked : CheckState.Checked;
                italicButton.CheckState = (int)(font.Style & FontStyle.Italic) == 0 ? CheckState.Unchecked : CheckState.Checked;
            }
        }

        /// <summary>
        /// Handles the SelectionChanged event of the rtDestination control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void rtDestination_SelectionChanged(object sender, EventArgs e)
        {
            this.UpdateFontControls(this.rtDestination.SelectionFont, this.cbDestinationFont, this.cbDestinationFontSize, this.btnDestinationBold, this.btnDestinationItalic);
        }

        /// <summary>
        /// Handles the CheckedChanged event of the btnSourceBold control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnSourceBold_CheckedChanged(object sender, EventArgs e)
        {
            this.ChangeFont(this.rtSource, this.cbSourceFont, this.cbSourceFontSize, this.btnSourceBold, this.btnSourceItalic);
        }

        /// <summary>
        /// Handles the CheckedChanged event of the btnSourceItalic control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnSourceItalic_CheckedChanged(object sender, EventArgs e)
        {
            this.ChangeFont(this.rtSource, this.cbSourceFont, this.cbSourceFontSize, this.btnSourceBold, this.btnSourceItalic);
        }

        /// <summary>
        /// Handles the CheckedChanged event of the btnDestinationBold control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnDestinationBold_CheckedChanged(object sender, EventArgs e)
        {
            this.ChangeFont(this.rtDestination, this.cbDestinationFont, this.cbDestinationFontSize, this.btnDestinationBold, this.btnDestinationItalic);
        }

        /// <summary>
        /// Handles the CheckedChanged event of the btnDestinationItalic control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnDestinationItalic_CheckedChanged(object sender, EventArgs e)
        {
            this.ChangeFont(this.rtDestination, this.cbDestinationFont, this.cbDestinationFontSize, this.btnDestinationBold, this.btnDestinationItalic);
        }

        /// <summary>
        /// Handles the KeyPress event of the rtSource control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.KeyPressEventArgs"/> instance containing the event data.</param>
        private void rtSource_KeyPress(object sender, KeyPressEventArgs e)
        {
            this.HandleHotKeys(e, this.rtSource, this.cbSourceFont, this.cbSourceFontSize, this.btnSourceBold, this.btnSourceItalic);
        }

        /// <summary>
        /// Handles the hot keys.
        /// </summary>
        /// <param name="e">Event arguments.</param>
        /// <param name="richTextBox">The rich text box.</param>
        /// <param name="fontList">The list of font family names.</param>
        /// <param name="fontSizeList">The list of font sizes.</param>
        /// <param name="boldButton">The "bold" button.</param>
        /// <param name="italicButton">The "italic" button.</param>
        private void HandleHotKeys(KeyPressEventArgs e, RichTextBox richTextBox, ToolStripComboBox fontList, ToolStripComboBox fontSizeList, ToolStripButton boldButton, ToolStripButton italicButton)
        {
            switch (Convert.ToInt32(e.KeyChar))
            {
                case 2:
                    boldButton.Checked = !boldButton.Checked;
                    this.ChangeFont(richTextBox, fontList, fontSizeList, boldButton, italicButton);
                    e.Handled = true;
                    break;

                case 9:
                    italicButton.Checked = !italicButton.Checked;
                    this.ChangeFont(richTextBox, fontList, fontSizeList, boldButton, italicButton);
                    e.Handled = true;
                    break;

                default:
                    e.Handled = false;
                    break;
            }
        }

        /// <summary>
        /// Handles the KeyPress event of the rtDestination control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.KeyPressEventArgs"/> instance containing the event data.</param>
        private void rtDestination_KeyPress(object sender, KeyPressEventArgs e)
        {
            this.HandleHotKeys(e, this.rtDestination, this.cbDestinationFont, this.cbDestinationFontSize, this.btnDestinationBold, this.btnDestinationItalic);
        }

        /// <summary>
        /// Handles the Click event of the btnTranscribe control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnTranscribe_Click(object sender, EventArgs e)
        {
            this.Transcribe();
        }

        /// <summary>
        /// Transcribes source text to destination.
        /// </summary>
        private void Transcribe()
        {
            try
            {
                this.Cursor = Cursors.WaitCursor;

                if (this.lstPlugins.Items.Count > 0)
                {
                    ITranscriberPlugin plugin = (ITranscriberPlugin)this.lstPlugins.SelectedItem;
                    string source = this.rtSource.SelectedText;

                    if ((null == source) || (0 == source.Length))
                    {
                        source = this.rtSource.Text;
                    }

                    this.rtDestination.SelectedText = plugin.Transcribe(source);
                }

            }
            finally
            {
                this.Cursor = Cursors.Default;
            }
        }

        /// <summary>
        /// Handles the Click event of the saveSourceAsToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void saveSourceAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                this.sourceFile = this.SaveAs("Save Source", this.rtSource);
                this.sourceChanged = false;
            }
            catch (AbortOperationException)
            {
            }
            catch
            {
                MessageBox.Show("Error saving file.", "Error", MessageBoxButtons.OK);
            }
        }

        /// <summary>
        /// Handles the Click event of the saveDestinationAsToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void saveDestinationAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                this.destinationFile = this.SaveAs("Save Destination", this.rtDestination);
                this.destinationChanged = false;
            }
            catch (AbortOperationException)
            {
            }
            catch
            {
                MessageBox.Show("Error saving file.", "Error", MessageBoxButtons.OK);
            }
        }

        /// <summary>
        /// Saves rich text to a new file.
        /// </summary>
        /// <param name="title">Dialog title</param>
        /// <param name="richTextBox">The rich text box.</param>
        /// <returns><see cref="FileInfo"/> for the new file.</returns>
        private FileInfo SaveAs(string title, RichTextBox richTextBox)
        {
            this.saveFileDialog1.Filter = "Rich Text Files (*.rtf)|*.rtf|Plain text files (*.txt)|*.txt|All files (*.*)|*.*";
            this.saveFileDialog1.Title = title;
            this.saveFileDialog1.CheckFileExists = false;

            if (DialogResult.OK != this.saveFileDialog1.ShowDialog())
            {
                throw new AbortOperationException();
            }

            if ((null == this.saveFileDialog1.FileName) || (0 == this.saveFileDialog1.FileName.Length))
            {
                throw new AbortOperationException();
            }

            FileInfo fileInfo = new FileInfo(this.saveFileDialog1.FileName);
            frmTranscriber.SaveRichTextBoxToFile(fileInfo, richTextBox);
            return fileInfo;
        }

        /// <summary>
        /// Handles the Click event of the transcribeToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void transcribeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Transcribe();
        }

        /// <summary>
        /// Handles the Click event of the btnPluginSettings control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnPluginSettings_Click(object sender, EventArgs e)
        {
            this.ShowSettingsDialog();
        }

        /// <summary>
        /// Handles the DoubleClick event of the lstPlugins control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void lstPlugins_DoubleClick(object sender, EventArgs e)
        {
            this.ShowSettingsDialog();
        }

        /// <summary>
        /// Shows the settings dialog for the currently selected plug-in.
        /// </summary>
        private void ShowSettingsDialog()
        {
            ITranscriberPlugin plugin = (ITranscriberPlugin)this.lstPlugins.SelectedItem;

            using (Form settingsDialog = plugin.GetSettingsDialog())
            {
                if (null == settingsDialog)
                {
                    MessageBox.Show(plugin + " has no user-configurable settings.", "No Settings");
                    return;
                }

                settingsDialog.ShowDialog();
            }
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the lstPlugins control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void lstPlugins_SelectedIndexChanged(object sender, EventArgs e)
        {
            ITranscriberPlugin plugin = (ITranscriberPlugin)lstPlugins.SelectedItem;
            this.rtSource.ContextMenuStrip = this.CreateSourceContextMenu();
            this.rtDestination.ContextMenuStrip = this.CreateDestinationContextMenu();
            Settings.Default.PluginName = plugin.Name;
        }

        /// <summary>
        /// Handles the Click event of the mnuCopy control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuSourceCopy_Click(object sender, EventArgs e)
        {
            this.rtSource.Copy();
        }

        /// <summary>
        /// Handles the Click event of the mnuCut control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuSourceCut_Click(object sender, EventArgs e)
        {
            this.rtSource.Cut();
        }

        /// <summary>
        /// Handles the Click event of the mnuPaste control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuSourcePaste_Click(object sender, EventArgs e)
        {
            this.rtSource.Paste();
        }

        /// <summary>
        /// Handles the Click event of the mnuPasteText control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuSourcePastePlainText_Click(object sender, EventArgs e)
        {
            this.rtSource.Paste(DataFormats.GetFormat(DataFormats.UnicodeText));
        }

        /// <summary>
        /// Handles the Click event of the mnuDestinationCopy control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuDestinationCopy_Click(object sender, EventArgs e)
        {
            this.rtDestination.Copy();
        }

        /// <summary>
        /// Handles the Click event of the mnuDestinationPaste control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuDestinationPaste_Click(object sender, EventArgs e)
        {
            this.rtDestination.Paste();
        }

        /// <summary>
        /// Handles the Click event of the mnuDestinationPastePlainText control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuDestinationPastePlainText_Click(object sender, EventArgs e)
        {
            this.rtDestination.Paste(DataFormats.GetFormat(DataFormats.UnicodeText));
        }

        /// <summary>
        /// Handles the Click event of the mnuDestinationCut control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void mnuDestinationCut_Click(object sender, EventArgs e)
        {
            this.rtDestination.Cut();
        }

        /// <summary>
        /// Gets or sets the source unicode text.
        /// </summary>
        /// <value>The source unicode text.</value>
        public string SourceText
        {
            get { return this.rtSource.Text; }
            set { this.rtSource.Text = value; }
        }

        /// <summary>
        /// Gets or sets the destination unicode text.
        /// </summary>
        /// <value>The destination unicode text.</value>
        public string DestinationText
        {
            get { return this.rtDestination.Text; }
            set { this.rtDestination.Text = value; }
        }

        /// <summary>
        /// Gets or sets the source rich text.
        /// </summary>
        /// <value>The source rich text.</value>
        public string SourceRtf
        {
            get { return this.rtSource.Rtf; }
            set { this.rtSource.Rtf = value; }
        }

        /// <summary>
        /// Gets or sets the destination rich text.
        /// </summary>
        /// <value>The destination rich text.</value>
        public string DestinationRtf
        {
            get { return this.rtDestination.Rtf; }
            set { this.rtDestination.Rtf = value; }
        }
    }
}